Bagaimana Anda menyukai opsi manajemen dependensi ini di Python?

Saya baru-baru ini memutuskan sudah saatnya untuk akhirnya memilah topik manajemen ketergantungan dalam proyek Python saya dan mulai mencari solusi yang benar-benar cocok untuk saya. Saya bereksperimen dengan pipenv, mempelajari dokumentasi puisi, membaca artikel terkait lainnya. Sayangnya, saya tidak pernah menemukan solusi yang sempurna. Akibatnya, saya menemukan sepeda baru, pendekatan saya, yang saya usulkan untuk dibahas di bawah potongan.


Masalah


Tetapi sebelum melanjutkan langsung ke deskripsi pendekatan, saya ingin menjelaskan mengapa kebutuhan seperti itu muncul dan mengapa solusi yang ada tidak cocok untuk saya.


Sebagai bagian dari pekerjaan saya, paling sering saya menggunakan Python untuk dua tujuan: analisis data dan pembelajaran mesin menggunakan notebook Jupyter, atau skrip Python kecil yang entah bagaimana menyiapkan data. Saya ingin mencatat bahwa saya tidak terlibat dalam pembuatan paket dan publikasi mereka di Pypi (karena itu, saya tidak fokus pada proses ini dalam artikel ini).


Sangat sering saya perlu menjalankan skrip atau notes yang dibuat sejak lama. Oleh karena itu, saya perlu memperbaiki versi dependensi dan menjalankan skrip dan notes di lingkungan virtual. Di sisi lain, kadang-kadang dalam versi baru perpustakaan, fungsionalitas dapat muncul yang akan meningkatkan hasil notepad atau skrip lama. Sebagai contoh, di scikit-learn (perpustakaan untuk pembelajaran mesin), mereka dapat menambahkan implementasi algoritma baru yang bekerja sangat baik untuk kasus saya.


Sangat sering, ketika mengembangkan skrip, saya juga harus menginstal beberapa dependensi tambahan yang hanya diperlukan untuk pengembangan. Misalnya, karena saya menggunakan VSCode untuk pengembangan, itu mengharuskan pylint diinstal di lingkungan virtual. Orang lain yang bekerja dengan saya dapat menggunakan alat pengembangan lain yang tidak memerlukan ketergantungan ini sama sekali.


Berdasarkan asumsi ini, saya telah mengembangkan persyaratan berikut untuk manajemen ketergantungan:


  1. Saya harus dapat memisahkan dependensi sentral (diperlukan untuk menjalankan skrip) dan dependensi yang diperlukan hanya untuk pengembangan.
  2. . , . , , .
  3. .

, ( pip freeze > requirements.txt) requirements.txt, . -, . -, requirements.txt , .


, pipenv (, , , ). , , . , , . , , . Poetry .



, , (k)Ubuntu 18.04. , . , , .


, . -, . : requirements.txt , requirements-dev.txt . , bash pip-install.


pip-install
function pip-install() {
    packages=()
    dev_dependency=0
    requirements_file=
    while [ $# -gt 0 ]
    do
        case "$1" in
            -h|--help)
                echo "Usage: pip-install [-d|--dev] [-r|--req <file>] <package1> <package2> ..." 1>&2
                echo ""
                echo "This function installs provided Python packages using pip"
                echo "and adds this dependency to the file listing requirements."
                echo "The name of the package is added to the file without"
                echo "concreate version only if it is absent there." 
                echo ""
                echo "-h|--help        - prints this message and exits."
                echo "-d|--dev         - if the dependency is development."
                echo "-r|--req <file>  - in which file write the dependency."
                echo "    If the filename is not provided by default the function"
                echo "    writes this information to requirements.txt or to"
                echo "    requirements-dev.txt if -d parameter is provided."
                echo "<package1> <package2> ..."
                return 0
                ;;
            -d|--dev)
                shift
                dev_dependency=1
                ;;
            -r|--req)
                shift
                requirements_file="$1"
                echo "Requirements file specified: $requirements_file"
                shift
                ;;
            *)
                packages+=( "$1" )
                echo "$1"
                shift
                ;;
        esac
    done

    if ! [ -x "$(command -v pip)" ]; then
        echo "Cannot find pip tool. Aborting!"
        exit 1
    fi

    echo "Requirements file: $requirements_file"
    echo "Development dependencies: $dev_dependency"
    echo "Packages: ${packages[@]}"

    if [ -z "$requirements_file" ]; then
        if [ $dev_dependency -eq 0 ]; then
            requirements_file="requirements.txt"
        else
            requirements_file="requirements-dev.txt"
        fi
    fi

    for p in "${packages[@]}"
    do
        echo "Installing package: $p"
        pip install $p
        if [ $? -eq 0 ]; then
            echo "Package installed successfully"
            echo "$p" >> $requirements_file
            if [ $(grep -Ec "^$p([~=!<>]|$)" "$requirements_file") -eq 0 ]; then
                echo "$p" >> $requirements_file
            else
                echo "Package $p is already in $requirements_file"
            fi
        else
            echo "Cannot install package: $p"
        fi
    done
}

, : pip-install scikit-learn pip-install --dev pylint. , scikit-learn pip ( ) requirements.txt. , requirements-dev.txt. , , , ( ). , .


, pip-freeze. pip-freeze requirements.txt, requirements.lock . , pip install -r requirements.lock. pip-freeze --dev , requirements-dev.txt.


pip-freeze
function pip-freeze() {
    dump_all=0
    dev_dependency=0
    requirements_file=
    while [ $# -gt 0 ]
    do
        case "$1" in
            -h|--help)
                echo "Usage: pip-freeze [-a|--all] [-d|--dev] [-r|--req <file>]" 1>&2
                echo ""
                echo "This function freezes only the top-level dependencies listed"
                echo "in the <file> and writes the results to the <file>.lock file."
                echo "Later, the data from this file can be used to install all"
                echo "top-level dependencies." 
                echo ""
                echo "-h|--help        - prints this message and exits."
                echo "-d|--dev         - if the dependency is development."
                echo "-a|--all         - if we should freeze all dependencies"
                echo "  (not only top-level)."
                echo "-r|--req <file>  - what file to use to look for the list of"
                echo "    top-level dependencies. The results will be written to"
                echo "    the \"<file>.lock\" file." 
                echo "    If the <file> is not provided by default the function"
                echo "    uses \"requirements.txt\" or \"requirements-dev.txt\""
                echo "    if -d parameter is provided and writes the results to the"
                echo "    \"requirements.txt.lock\" or \"requirements-dev.txt.lock\""
                echo "    correspondingly."
                return 0
                ;;
            -d|--dev)
                shift
                echo "Development dependency"
                dev_dependency=1
                ;;
            -a|--all)
                shift
                dump_all=1 
                ;;
            -r|--req)
                shift
                requirements_file="$1"
                echo "Requirements file specified: $requirements_file"
                shift
                ;;
        esac
    done

    if ! [ -x "$(command -v pip)" ]; then
        echo "Cannot find pip tool. Aborting!"
        exit 1
    fi

    if [ -z "$requirements_file" ]; then
        if [ $dev_dependency -eq 0 ]; then
            requirements_file="requirements.txt"
        else
            requirements_file="requirements-dev.txt"
        fi
    fi

    lock_file="$requirements_file.lock"
    if [ $dump_all -eq 1 ] 
    then
        pip freeze > "$lock_file"
        if [ $? -eq 0 ]; then
            echo "Locked all dependencies to: $lock_file"
        else
            echo "Error happened while locking all dependencies"
        fi
    else
        cmd_output=$(pip freeze -r "$requirements_file")
        if [ $? -eq 0 ]; then
            > "$lock_file"
            while IFS= read -r line; do
                if [ "$line" = "## The following requirements were added by pip freeze:" ]; then
                    break
                fi
                echo "$line" >> "$lock_file"
            done <<< "$cmd_output"
        fi
    fi
}

, 4 : requirements.txt, requirements-dev.txt, requirements.lock requirements-dev.lock.


Kode sumber dari kedua fungsi ini disimpan dalam file saya (Selalu periksa sumbernya !!!). Anda dapat menyalinnya ke direktori ~/.bash/, dan untuk membuat fungsi-fungsi ini tersedia di rumah, tambahkan baris berikut untuk diri sendiri di .bashrc:


if [ -f ~/.bash/pip_functions.sh ]; then
    source ~/.bash/pip_functions.sh
fi

Kesimpulan


Saya baru saja mulai menggunakan solusi ini dan mungkin saya belum menemukan kekurangan. Secara umum, pos ini disusun untuk membahas pendekatan ini dan apakah ia memiliki hak untuk hidup. Jika Anda melihat masalah, saya akan sangat senang mendengarnya.


PS

, Python , ( ) .


All Articles